#include <xen/config.h>
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/errno.h>
#include <xen/compile.h>
#include <xen/sched.h>
#include <xen/shadow.h>
#include <xen/guest_access.h>
#include <xen/softirq.h>
#include <asm/current.h>
#include <public/nmi.h>
#include <public/version.h>
#include <xen/serial.h>
#include <goldfish/head.h>
#include <goldfish/drawchar.h>
#include <goldfish/fb.h>
#include <asm-arm/memmap.h>
#include <xen/foreground.h>

#include <public/goldfish_drivers_types.h>

#include <fs/namei.h>
#include <fs/fs.h>
#include <xen/types.h>

#define BUFLEN 1024
#define initrd_addr MEMMAP_GUEST_0_RAMDISK_COPY_PADDR 

static char buf[BUFLEN];
extern struct domain *domian_list;

extern struct timer fresh_timer;

static int flag_monitor = 0;


struct Path
{
	char path[BUFLEN];
	int pos;//current pos in path
};
struct Path current_path = {
.path = "/",
.pos = 0
};

struct domain_addr_set {
        unsigned long   guest_memory_start;
        unsigned long   guest_memory_size;
        unsigned long   elf_image_address;
        unsigned long   elf_image_size;
        unsigned long   initrd_address;
        unsigned long   initrd_size;
        unsigned long   command_line_address;
        unsigned long   stack_start;
};

struct bvt_vcpu_info
{
    struct list_head    run_list;         /* runqueue list pointers */
    u32                 avt;              /* actual virtual time */
    u32                 evt;              /* effective virtual time */
    int                 migrated;         /* migrated to a new CPU */
    struct vcpu         *vcpu;
    struct bvt_dom_info *inf;
};
#define EBVT_INFO(p)  ((struct bvt_vcpu_info *)(p)->sched_priv)
extern void __enter_scheduler();
int flag_grab = 0;

extern struct domain_addr_set domain_addrs[4];

extern unsigned int foreground_domain_copy;
extern struct event_info switch_info[10];

#define WHITESPACE "\t\r\n "
#define MAXARGS 16

int mon_help(int argc, char** argv);
int mon_run(int argc, char** argv);
int mon_list(int argc, char** argv);
int mon_boot(int argc, char** argv);
int mon_kill(int argc, char** argv);
int mon_mem(int argc, char** argv);
int mon_cli(int argc, char** argv);
int mon_stop(int argc, char** argv);
int mon_recover(int argc, char** argv);
int mon_ls2(int argc, char **argv);
int mon_cat(int argc, char** argv);

int mon_ls(int argc, char * argv[])
{
	if(argc < 1 || argc >2)
	{
		goto ls_error;
	}

	if(argc == 1)//分两种情况一种是当前路径为空，另一种是非空
	{
		if(0 == current_path.pos)
		{
			print("system\n");
			print("userdata\n");
			print("cache\n");
		}
		else
		{
			dumpDir(current_path.path);
		}
	}
	if(argc == 2)
	{
		if(0 == current_path.pos || '/' == argv[1][0] )
		{
			if(!strcmp(argv[1], '/'))
			{
				printk("system\n");
				printk("userdata\n");
				printk("cache\n");
				return 0;
			}
			printk("%s\n", argv[1]);
			dumpDir(argv[1]);
		}
		else
		{
			char str[1000];
			if(current_path.path[current_path.pos] != '/' )
			{
				sprintf(str, "%s/%s", current_path.path, argv[1]);
			}
			else
			{
				sprintf(str, "%s%s", current_path.path, argv[1]);

			}
			dumpDir(str);
		}
//		printk("*********\n");
	}
	return 0;
ls_error:
	print("error");
	return 0;
}
int mon_pwd(int argc, char * argv[])
{
	if(argc != 1)
		goto pwd_error;
	else
		print(current_path.path);
		print("\n");
	return 0;
pwd_error:
	print("too many arguments\n");
	return 0;

}
int mon_cd(int argc, char * argv[])
{
	if(argc > 2)
		goto cd_error;
	if(2 == argc)
	{//首先要判断一下argv[1]，所指的目录是否存在
		char str[1000];
		if('/' == argv[1][0])
		{
			//printk("strcmp:%d\n",strcmp(argv[1], "/"));
			if(!strcmp(argv[1], "/"))
			{
//				printk("gen\n");
				strcpy(current_path.path, argv[1]);
				current_path.pos = 0;
				current_path.path[1] = '\0';
			}
			else
			{//若!= '/'
				if(!yaffs_opendir(argv[1]))
					goto cd_error;
				strcpy(current_path.path, argv[1]);
				current_path.pos = strlen(current_path.path) - 1;
				current_path.pos++;
				current_path.path[current_path.pos++] = '/';
				current_path.path[current_path.pos] = '\0';
			}
			//printk("new path len:%d\n", current_path.pos);
			return 0;
		}
		else
		{
//			if(current_path.pos == 0)
				sprintf(str, "%s%s", current_path.path, argv[1]);
//			else	
//				sprintf(str, "%s/%s", current_path.path, argv[1]);
			printk("str:%s\n", str);
			if(!yaffs_opendir(str))
				goto cd_error;
		//	printk("str:%s, strlen:%d\n", str, strlen(str));
			strcpy(current_path.path, str);
			current_path.pos = strlen(str) - 1;
			current_path.pos++;
			current_path.path[current_path.pos++] = '/';
			current_path.path[current_path.pos] = '\0';
	//		printk("new path:%s\n", current_path.path);
		}
	}
			mon_pwd(1,&current_path.path);
	return 0;
cd_error:
	print("cannot open\n");
	return 0;
}

//extern struct domain_addr_set domain_addrs[4];

struct Command {
        const char *name;
        const char *desc;
        // return -1 to force monitor to exit
        int (*func)(int argc, char** argv);
};

static struct Command commands[] = {
        { "help", "list all the command", mon_help},
        { "run", "start to schedule domain", mon_run},
        { "list", "list all the vm", mon_list},
        { "boot", "boot the domain", mon_boot},
        { "kill", "destroy a domain", mon_kill},
        { "mem", "mem", mon_mem},
        { "cli", "clear the screen", mon_cli},
	{ "stop", "pause one domain", mon_stop},
        { "recover", "unpause one domain", mon_recover},
	{ "ls", "list the files of given path", mon_ls},
	{ "pwd", "give the current path", mon_pwd},
	{ "cd", "change directory", mon_cd},
	{"ls2", "show a directory", mon_ls2},
	{"cat", "show a file", mon_cat}
};
#define NCOMMANDS (sizeof(commands)/sizeof(commands[0]))

unsigned int cr2_read(void)
{
        unsigned int adr;

         __asm__ __volatile__(
"       mrc     p15, 0, %0, c2, c0, 0"
        : "=r" (adr)
        :
        : "memory", "cc");
        return adr;
}

extern struct fb_image pic;
extern u8 rec[2000];
int mon_cli(int argc, char** argv)
{
	int i;
        for(i = 0; i < 2000; i++)
		rec[i] = 0;
	testfb();
        pic.dx = 0;
        pic.dy = 0;
	print("Welcome to arm hyperviser!\n");
	return 0;	
}

int flag_stop = 0;
int mon_stop(int argc, char** argv)
{
       //not completed
       struct domain *d;
       struct domain *dom;
       unsigned int domid;
       char *end;
       if(argc != 2)
       {
               print("bad param\n");
               return 0;
       }
       domid = simple_strtoul(argv[1], &end, 10);
       if(end == argv[1])
       {
               print("bad param\n");
               return 0;
       }

       if(domid != 0 && domid != 1)
       {
               print("bad param\n");
               return 0;
       }
       d = find_domain_by_domid(domid);
       if(!d)
       {
               print("this domain doesn't exist\n");
               return 0;
       }
       /*if(d->vcpu[0] == current)
       {
               print("don't try to stop the current domain\n");
               return 0;
       } 
       domain_pause_by_systemcontroller(d);
       return 0;*/
	dom = find_domain_by_domid((domid+1)%2);
        if(d->vcpu[0] == current)
        {printk("current domid %d\n", current->domain->domain_id);
                if(dom && !test_bit(_DOMF_ctrl_pause, &dom->domain_flags)){
                        EBVT_INFO(dom->vcpu[0])->evt = 0;
                        flag_stop = d->domain_id + 1;
                        __enter_scheduler();
			return 1;
                }
                print("don't try to stop the current domain\n");
                return 0;
        }

	domain_pause_by_systemcontroller(d);
        if(foreground_domain_copy && domid == 1)//switch foreground_domain
                send_guest_virq(dom->vcpu[0], VIRQ_SWITCH);
        foreground_domain_copy = (domid + 1)%2;
        return 0;
}

int mon_recover(int argc, char** argv)
{
       //not completed
       struct domain *d;
       unsigned int domid;
       char *end;
       if(argc != 2)
       {
               print("bad param\n");
               return 0;
       }
       domid = simple_strtoul(argv[1], &end, 10);
       if(end == argv[1])
       {
               print("bad param\n");
               return 0;
       }

       if(domid != 0 && domid != 1)
       {
               print("bad param\n");
               return 0;
       }
       d = find_domain_by_domid(domid);
       if(!d)
       {
               print("this domain doesn't exist\n");
               return 0;
       }
       domain_unpause_by_systemcontroller(d);
       return 0;
}

int mon_mem(int argc, char** argv)
{
        /*unsigned int *ptr = 0xc00300b0;
        unsigned int *ptr1 = (*ptr)&(~0xfff) -0x200000 + 0xc0000000;
        printk("ptr=%x\n", *ptr);*/
        unsigned int p = cr2_read();
        printk("pd=%x\n", p);
        if(p == 0x230000)
              printk("adr=%x\n", *((unsigned int*)0xc00300b0));
        else if(p == 0x2038000)
              printk("adr=%x\n", *((unsigned int*)0xc0038070));
        else
        {
              printk("adr=%x\n", *((unsigned int*)0x000c070));
              printk("adr_dom0elf=%x\n", *((unsigned int*)0x1c00000));
              printk("adr_dom0=%x\n", *((unsigned int*)0x2300b0));
              printk("adr_dom1=%x\n", *((unsigned int*)0x2038070));
        }
        return 0;
}

int mon_help(int argc, char **argv)
{
        int i;
        for(i = 0; i < NCOMMANDS; i++)
        {
                print("%s   %s\n", commands[i].name, commands[i].desc);
        }
        return 0;
}

int mon_run(int argc, char **argv)
{
	struct domain *d = domain_list;
	struct domain *dom0 = find_domain_by_domid(0);
	if(d != NULL && foreground_domain == MAX_DOMAIN_NUM)
		foreground_domain = foreground_domain_copy ;
	if(foreground_domain == MAX_DOMAIN_NUM)
	{
		print("no domain exist\n");
		return 0;
	}
	if(foreground_domain){
		set_timer(&fresh_timer, NOW() + MILLISECS(100));
		//testfb_dom();
		return 1;
	}
	else if(dom0 && dom0->scan_code){
		//memcpy((void*)(dom0->scan_code), (const void*)switch_info, 10*sizeof(struct event_info));
		memcpy(/*(void*)d->scan_code*/(void*)dom0->shared_info + 0xd00, (const void*)switch_info, 10*sizeof(struct event_info));
                send_guest_virq(dom0->vcpu[0], VIRQ_KEYBOARD);	
	}
	set_timer(&fresh_timer, NOW() + MILLISECS(100));
	/*d = find_domain_by_domid(foreground_domain);
	if(d != NULL)
	{
		*(d->status) = 100;
		send_guest_virq(d->vcpu[0], VIRQ_FB);
	}*/
        return 1;
}

int mon_list(int argc, char **argv)
{ 
        struct domain *d = domain_list;
        while(d)
        {
                 print("dom%d      %s\n", d->domain_id, test_bit(_DOMF_ctrl_pause, &d->domain_flags)? "pause": "runnable");
                 d = d->next_in_list;
        }
        return 0;
}

int flag_dom0 = 0;
int mon_boot(int argc, char **argv)
{unsigned long flags;
//local_irq_save(flags);
        struct domain *d;
        unsigned int domid;
        char *end;
        if(argc != 2)
        {
                print("bad param\n");
                return 0;
        }
        domid = simple_strtoul(argv[1], &end, 10);
        if(end == argv[1])
        {
                print("bad param\n");
                return 0;
        }

        if(domid != 0 && domid != 1)
        {
                print("bad param\n");
                return 0;
        }
        if(domid == 1 && find_domain_by_domid(0) == NULL)
        {
                print("dom0 must be booted first\n");
                return 0;
        }
         
        d = domain_create(domid, 0);
        if(!domid)
                dom0 = d;         

        if ( d == NULL )
                printk("Error creating domain 0\n");

        DPRINTK(3,"\n\n Construct domain 0 \n\n");

        if(domid == 0){
		if(flag_dom0)//reserve the ramdisk
			memcpy((void*)domain_addrs[domid].initrd_address, (const void*)initrd_addr, domain_addrs[domid].initrd_size);
		else
			memcpy((void*)initrd_addr, (const void*)(domain_addrs[domid].initrd_address), domain_addrs[domid].initrd_size);
                if ( construct_dom0(d,
                        domain_addrs[domid].guest_memory_start,
                        domain_addrs[domid].guest_memory_size,
                        domain_addrs[domid].elf_image_address,
                        domain_addrs[domid].elf_image_size,
                        domain_addrs[domid].initrd_address,
                        domain_addrs[domid].initrd_size,
                        "qemu=1 console=ttyS0 android.checkjni=1 android.qemud=ttyS1") != 0)                          
                              printk("Could not set up DOM%d guest OS\n", domid);
		else{
			flag_dom0 = 1;
			foreground_domain_copy = 0;
		}
	}

        if(domid == 1)
                if ( construct_guest_dom(d,
                        domain_addrs[domid].guest_memory_start,
                        domain_addrs[domid].guest_memory_size,
                        domain_addrs[domid].elf_image_address,
                        domain_addrs[domid].elf_image_size,
                        domain_addrs[domid].initrd_address,
                        domain_addrs[domid].initrd_size,
                        NULL) != 0)
                              printk("Could not set up DOM%d guest OS\n", domid);

        DPRINTK(3," dom0 execution envirionment configuration!! \n");
        
        set_bit(_DOMF_privileged, &d->domain_flags);

        domain_unpause_by_systemcontroller(d);

//local_irq_restore(flags);
//printk("hello world\n");
        return 0;
}

int mon_kill(int argc, char **argv)
{
        char *end;
        struct domain *d;
	struct domain *dom;
 
        if(argc != 2)
        {
                print("bad param\n");
                return 0;
        }
        unsigned int domid = simple_strtoul(argv[1], &end, 10);
        
        if(end == argv[1])
        {
                print("bad param\n");
                return 0;
        }
        d = find_domain_by_domid(domid);
	if(!d)
	{
		print("this domain doesn't exist\n");
                return 0;
	}
	dom = find_domain_by_domid((domid+1)%2);
        if(d->vcpu[0] == current)
        {
		if(dom && !test_bit(_DOMF_ctrl_pause, &dom->domain_flags)){
			EBVT_INFO(dom->vcpu[0])->evt = 0;
			flag_grab = d->domain_id + 1;
			__enter_scheduler();
		}
                print("don't try to kill the current domain\n");
                return 0;
        }      
 
        domain_kill(find_domain_by_domid(domid));
	if(foreground_domain_copy && domid == 1)//switch foreground_domain
                send_guest_virq(dom->vcpu[0], VIRQ_SWITCH);
        foreground_domain_copy = (domid + 1)%2;
        return 0;
}

int mon_ls2(int argc, char **argv)
{
        char *name;
        if(argc != 2)
        {
                print("bad param\n");
                return 0;
        }
        name = argv[1];
        struct nameidata *nd = xmalloc_bytes(sizeof(struct nameidata));
        path_lookup(name,0,nd);
        struct inode *dir = nd->path.dentry->d_inode;
        fat_readdir(dir);
        xfree(nd);
        return 0;
}

int mon_cat(int argc, char **argv)
{
        char *name;
        if(argc !=2)
        {
                print("bad param\n");
                return 0;
        }
        name = argv[1];
        struct nameidata *nd = xmalloc_bytes(sizeof(struct nameidata));
        path_lookup(name, 0, nd);
        struct inode *inode = nd->path.dentry->d_inode;
        xfree(nd);
        char *buf = xmalloc_bytes(inode->i_size);
        my_fat_do_sync_read(inode, buf, 0, 0);
        print("%s", buf);
        xfree(buf);
        return 0;
}

int cons_getc(void)
{
        int c;
//printk("get wpos = %d\n", cons.wpos);
        // grab the next character from the input buffer.
        if (cons.rpos != cons.wpos) {
                c = cons.buf[cons.rpos++];
                if (cons.rpos == CONSBUFSIZE)
                        cons.rpos = 0;
                return c;
        }
        return 0;
}

int getchar(void)
{
        int c;

        while ((c = cons_getc()) == 0)
                /* do nothing */;
        return c;
}

char * readline(const char *prompt)
{
        int i ,c;
        char *s;

        struct serial_port *port = &com[0];

        if (prompt != NULL){
                s = prompt;
                //while(*s != '\0'){
                      //__raw_writel(*s, 0xf0002000);
                      print("%s", s);
                      //port->driver->putc(port, *s);
                      //s++;
                //}
        }

        i = 0;

        while (1) {
                c = getchar();
                if (c < 0) {
                        print("read error\n");
                        return NULL;
                } else if (c >= ' ' && i < BUFLEN-1) {
                        print("%c", (char)c);
                        buf[i++] = (char)c;
                } else if (c == '\b' && i > 0) {
                        print("%c", (char)c);
                        i--;
                } else if (c == '\n' || c == '\r') {
                        print("\n");
                        buf[i] = 0;
                        return buf;
                }
        }
}

int runcmd(char *buf)
{
        int argc;
        char *argv[MAXARGS];
        int i;

        // Parse the command buffer into whitespace-separated arguments
        argc = 0;
        argv[argc] = 0;
        while (1) {
                // gobble whitespace
                while (*buf && strchr(WHITESPACE, *buf))
                        *buf++ = 0;
                if (*buf == 0)
                        break;

                // save and scan past next arg
                if (argc == MAXARGS-1) {
                        print("Too many arguments (max %d)\n", MAXARGS);
                        return 0;
                }
                argv[argc++] = buf;
                while (*buf && !strchr(WHITESPACE, *buf))
                        buf++;
        }
        argv[argc] = 0;

        // Lookup and invoke the command
        if (argc == 0)
                return 0;
        for (i = 0; i < NCOMMANDS; i++) {
                if (strcmp(argv[0], commands[i].name) == 0)
                        return commands[i].func(argc, argv);
        }
        print("Unknown command '%s'\n", argv[0]);
        return 0;
}

void monitor(void)
{
        char *buf;
	if(!flag_monitor){
	        print("Welcome to arm hyperviser!\n");
		flag_monitor = 1;
	}
	cons.rpos = 0;
        cons.wpos = 0;

        while (1) {
                buf = readline("neo>");
                if (buf != NULL)
                        if (runcmd(buf) == 1)
                                break;
        }
}

extern int sched_flag;
extern void testfb();
extern struct fb_image pic;
void console_softirq_action(void)
{
        sched_flag = 1;
        raise_softirq(SCHEDULE_SOFTIRQ);
		testfb();
}








